package com.xz.common.forward;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author renyong
 * @date 2023/5/11 10:01
 * @description 请求转发
 * 参考文档 https://zhuanlan.zhihu.com/p/299396304
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class RoutingDelegate {
    /**
     * http调用
     */
    private final RestTemplate restTemplate;

    public ResponseEntity<String> redirect(HttpServletRequest request, HttpServletResponse response, String routeUrl, String prefix) {
        try {
            // build up the redirect URL
            String redirectUrl = createRedictUrl(request,routeUrl, prefix);
            log.info("请求转发到 {}",redirectUrl);
            RequestEntity requestEntity = createRequestEntity(request, redirectUrl);
            return route(requestEntity);
        } catch (Exception e) {
            return new ResponseEntity("REDIRECT ERROR", HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    private String createRedictUrl(HttpServletRequest request, String routeUrl, String prefix) {
        String queryString = request.getQueryString();
        return routeUrl + request.getRequestURI().replace(prefix, "") +
                (queryString != null ? "?" + queryString : "");
    }


    private RequestEntity createRequestEntity(HttpServletRequest request, String url) throws URISyntaxException, IOException {
        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
        MultiValueMap<String, String> headers = parseRequestHeader(request);
        Object body = IOUtils.toByteArray(request.getInputStream());
        if (MediaType.APPLICATION_FORM_URLENCODED_VALUE.equals(request.getContentType())) {
            // 如果是表单请求，就把字段放到请求头传过来
            log.info("请求转发-表单请求");
            Map<String, String[]> parameterMap = request.getParameterMap();
            MultiValueMap<Object, Object> params = new LinkedMultiValueMap<>();
            Set<Map.Entry<String, String[]>> entrySet = parameterMap.entrySet();
            for(Map.Entry<String, String[]> entry : entrySet){
                params.add(entry.getKey(),entry.getValue()[0]);
            }
            headers.add(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_FORM_URLENCODED_VALUE);
            body = params;
        }
        RequestEntity<Object> objectRequestEntity = new RequestEntity<>(body, headers, httpMethod, new URI(url));
        return objectRequestEntity;
    }

    private ResponseEntity<String> route(RequestEntity requestEntity) {
        log.info("请求转发-发送请求");
        return restTemplate.exchange(requestEntity, String.class);
    }

    private MultiValueMap<String, String> parseRequestHeader(HttpServletRequest request) {
        HttpHeaders headers = new HttpHeaders();
        List<String> headerNames = Collections.list(request.getHeaderNames());
        for (String headerName : headerNames) {
            List<String> headerValues = Collections.list(request.getHeaders(headerName));
            for (String headerValue : headerValues) {
                headers.add(headerName, headerValue);
            }
        }
        return headers;
    }
    /**
     * 获取请求body
     * @param request
     * @return
     * @author renyong on 2023/5/11 15:30
     */
    public String getBody(HttpServletRequest request) throws IOException {
        ServletInputStream ris = request.getInputStream();
        StringBuilder content = new StringBuilder();
        byte[] b = new byte[1024];
        int lens = -1;
        while ((lens = ris.read(b)) > 0) {
            content.append(new String(b, 0, lens));
        }
        return content.toString();
    }
}
